﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Clandestine;

namespace Slick
{
    public class Layer
    {
        private decimal depth;

        public decimal Depth
        {
            get { return depth; }
            set
            {
                depth = value;
                lock (Scanlines)
                    foreach (Dictionary<int, Sprite> dis in Scanlines.Values)
                        foreach (Sprite sp in dis.Values)
                            sp.Layer = ((float)value);
            }
        }

        public Dictionary<int, Dictionary<int, Sprite>> Scanlines = new Dictionary<int, Dictionary<int, Sprite>>();

        public void SetTile(int tileX, int tileY, Texture newTile)
        {
            lock (Scanlines)
            {
                if (!Scanlines.ContainsKey(tileY))
                    Scanlines.Add(tileY, new Dictionary<int, Sprite>());

                if (Scanlines[tileY].ContainsKey(tileX))
                {
                    Scanlines[tileY][tileX].SetTexture(newTile);
                    Scanlines[tileY][tileX].Visible = true;
                    Scanlines[tileY][tileX].Layer = ((float)Depth);
                }
                else
                {
                    Scanlines[tileY].Add(tileX, new Sprite(newTile));
                    Scanlines[tileY][tileX].X = tileX * Program.GridSize;
                    Scanlines[tileY][tileX].Y = tileY * Program.GridSize;
                    Scanlines[tileY][tileX].Layer = ((float)Depth);
                }
            }
        }

        public void ClearTile(int tileX, int tileY)
        {
            lock (Scanlines)
            {
                if (!Scanlines.ContainsKey(tileY))
                    return;

                if (Scanlines[tileY].ContainsKey(tileX))
                {
                    Scanlines[tileY][tileX].Dispose();
                    Scanlines[tileY].Remove(tileX);
                }
            }
        }

        public void Dispose()
        {
            lock (Scanlines)
            {
                lock (Graphics.Renderables) // WAY too much overhead if we keep locking in .Dispose()!
                {
                    foreach (Dictionary<int, Sprite> dis in Scanlines.Values)
                        foreach (Sprite s in dis.Values)
                            s.Dispose();
                }
                Scanlines = new Dictionary<int, Dictionary<int, Sprite>>();
            }
        }

        public void HideEverything()
        {
            lock (Scanlines)
            {
                foreach (Dictionary<int, Sprite> dis in Scanlines.Values)
                    foreach (Sprite s in dis.Values)
                        s.Visible = false;
                //Scanlines = new Dictionary<int, Dictionary<int, Sprite>>();
            }
        }

        public void ShowEverything()
        {
            lock (Scanlines)
            {
                foreach (Dictionary<int, Sprite> dis in Scanlines.Values)
                    foreach (Sprite s in dis.Values)
                        s.Visible = true;
            }
        }

        public int GetMinLine()
        {
            lock (Scanlines)
                if (Scanlines.Keys.Count != 0)
                    return (Scanlines.Keys.Min() < 0 ? Scanlines.Keys.Min() : 0);
            return 0;
        }

        public int GetMaxLine()
        {
            lock (Scanlines)
                if (Scanlines.Keys.Count != 0)
                    return Scanlines.Keys.Max();
            return 0;
        }

        public int GetMinX()
        {
            lock (Scanlines)
            {
                int minx = 0;

                foreach (Dictionary<int, Sprite> dis in Scanlines.Values)
                {
                    if (dis.Count == 0)
                        continue;

                    int a = dis.Keys.Min();
                    if (a < minx)
                        minx = a;
                }

                return minx;
            }
        }

        public int GetMaxX()
        {
            lock (Scanlines)
            {
                int maxx = 0;

                foreach (Dictionary<int, Sprite> dis in Scanlines.Values)
                {
                    if (dis.Count == 0)
                        continue;

                    int a = dis.Keys.Max();
                    if (a > maxx)
                        maxx = a;
                }

                return maxx;
            }
        }

        public void Open(string layerData)
        {
            // Split into lines
            string[] lines = layerData.Trim().Split(new string[] { "\r\n" }, StringSplitOptions.None);

            for (int y = 0; y < lines.Length; y++)
            {
                // Only do something if it's not a blank line
                if ((lines[y] != ". . . ") && (lines[y].Trim() != string.Empty)) // where the string.Empty every alternate line comes in, I don't know. .Split()?
                {
                    string[] splitLine = lines[y].Trim().Split(" ".ToCharArray());

                    // Set each tile
                    for (int x = 0; x < splitLine.Length; x++)
                    {
                        if (splitLine[x] != ".")
                        {
                            string[] splitTile = splitLine[x].Split(".".ToCharArray());
                            if (splitTile.Length > 2)
                                throw new Exception("wtf mate. ^.^");

                            // Get tileset name..
                            splitTile[0] = Tilesets.LoadedTilesets[int.Parse(splitTile[0])];

                            // Set the tile, and we're done!
                            if (!Texture.TextureExists(splitTile[0] + "!" + splitTile[1]))
                                Tilesets.CutSingleTile(splitTile[0], int.Parse(splitTile[1]));
                            this.SetTile(x, y, Texture.GetTexture(splitTile[0] + "!" + splitTile[1]));
                        }
                    }
                }
            }
        }

        public void Save(System.Xml.XmlTextWriter xml, int minline, int maxline, int minx, Dictionary<string, int> tilesetToIndexMapping)
        {
            // Start the layer tag and shizz.
            xml.WriteStartElement("layer");
            xml.WriteAttributeString("depth", Depth.ToString());
            StringBuilder sb = new StringBuilder();

            // Convert the layer to text!
            lock (Scanlines)
            {
                for (int y = minline; y <= maxline; y++)
                {
                    if (!Scanlines.Keys.Contains(y) || Scanlines[y].Keys.Count == 0)
                        sb.AppendLine(". . . ");
                    else
                    {
                        string line = "";

                        for (int x = minx; x <= Scanlines[y].Keys.Max(); x++)
                        {
                            if (!Scanlines[y].Keys.Contains(x))
                                line += ". ";
                            else
                            {
                                string tilename = Scanlines[y][x].GetTexture().Filename;

                                int li = tilename.LastIndexOf('!');
                                string tilesheetName = tilename.Substring(0, li);
                                string tileNumber = tilename.Substring(li + 1);

                                line += tilesetToIndexMapping[tilesheetName].ToString()
                                    + "." + tileNumber + " ";
                            }
                        }

                        sb.AppendLine(line);
                    }
                }

            }

            // Tidy up
            xml.WriteCData(sb.ToString());
            xml.WriteEndElement(); // </layer>
        }
    }
}
